package Timer_TimerTask;

import java.util.concurrent.PriorityBlockingQueue;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lishuo
 * Date: 2023-04-05
 * Time: 12:30
 */

// 两个优化：
//    1、添加比较方式
//    2、解决忙等问题

// 加锁只是为了 wait 和 notify    ！！！

// MyTask表示一个任务
class MyTask implements Comparable<MyTask> {
    public Runnable runnable;
    public long time;
    // 为了方便后续判定, 使用绝对的时间戳

    public MyTask(Runnable runnable, long delay) {
        this.runnable = runnable;
        // 取当前时刻的时间戳 + delay, 作为该任务实际执行的时间戳
        this.time = System.currentTimeMillis() + delay;
    }

    @Override
    public int compareTo(MyTask o) {
        // 这样的写法意味着每次取出的是时间最小的元素.
        return (int) (this.time - o.time);
    }
}

public class MyTimer {
    // 这个结构, 带有优先级的阻塞队列. 核心数据结构
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();

    // 创建一个锁对象
    Object locker = new Object();

    public void schedule(Runnable runnable, long delay) {
        // synchronized (locker) { 锁加到这里为什么有bug
        // 答：经过简单分析得出——>如果锁加在这里，在TestMyTimer的main方法中，
        // 在实例化MyTimer对象时，由于先执行构造方法，且一开始queue一定为空，
        // 导致队列queue阻塞，由于构造方法中的queue.take()和此处均为locker
        // 加上了锁，queue阻塞locker不会解锁，由于此处也以locker为锁对象，
        // 就导致这里的put不能执行——死锁

        // 根据参数, 构造 MyTask, 插入队列即可.
        MyTask myTask = new MyTask(runnable, delay);
        queue.put(myTask);

        synchronized (locker) {
            locker.notify();
        }
    }

    // 在这里构造线程, 负责执行具体任务
    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    synchronized (locker) {
                        MyTask myTask = queue.take();
                        long curTime = System.currentTimeMillis();
                        if (myTask.time <= curTime) {
                            // 时间到了, 可以执行任务了
                            myTask.runnable.run();
                        } else {
                            // 时间还没到
                            queue.put(myTask);
                            locker.wait(myTask.time - curTime);
                        }
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        // 执行线程
        t.start();
    }

}
